#! /usr/bin/python
# -*- coding:utf8 -*- 

import Queue
import math

from PyQt4 import QtCore, QtGui
from PyQt4.QtCore import *
from PyQt4.QtGui import *

class MainScene(QGraphicsScene):
    def __init__(self, parent=None):
        super(MainScene, self).__init__(parent)
        self.__initObjects()
        self.__setObjects()

        self.info_timer.timeout.connect(self.info)
        self.animate_timer.timeout.connect(self.animate)
    
    def __initObjects(self):
        self.is_get_initial_points = False
        self.is_get_end_points = False
        self.initial_points = []
        self.end_points = []
        self.inter_points = []
        self.all_points = []
        self.animate_count = 0
        self.info_timer = QTimer()
        self.animate_timer = QTimer()
        self.info_queue = Queue.Queue()    #       
        self.info_item = QGraphicsTextItem()

    def __resetData(self):
        self.is_get_initial_points = False
        self.is_get_end_points = False
        self.initial_points = []
        self.end_points = []
        self.inter_points = []
        self.all_points = []
        self.animate_count = 0
        self.info_queue = Queue.Queue()    #       

    def __setObjects(self):
        self.__setSpecialItem()

    def __setSpecialItem(self):
        self.info_item = QGraphicsTextItem()
        self.info_item.setPos(0, 0)
        self.addItem(self.info_item)
        hand_pixmap = QPixmap('./hand.png')
        self.hand_item = QGraphicsPixmapItem(hand_pixmap)
        self.hand_item.setOffset(QPointF(100,200))
        self.addItem(self.hand_item)

    def get_initial_points(self):
        if self.is_get_end_points:
            self.is_get_end_points = not self.is_get_end_points
        self.is_get_initial_points = not self.is_get_initial_points

    def get_end_points(self):
        if self.is_get_initial_points:      # set is_get_initial_points False
            self.is_get_initial_points = not self.is_get_initial_points
        self.is_get_end_points = not self.is_get_end_points

    def mousePressEvent(self, QGraphicsSceneMouseEvent):
        if QGraphicsSceneMouseEvent.button() == Qt.LeftButton:
            if self.is_get_initial_points:
                pos = QGraphicsSceneMouseEvent.scenePos()
                color = QColor(Qt.darkRed)
                self.addEllipse(QRectF(pos,QSizeF(3.0,3.0)),
                        QPen(color), QBrush(color))
                points_count = len(self.initial_points)
                if points_count:
                    self.addLine(QLineF(self.initial_points[points_count-1], pos))
                self.initial_points.append(pos)
                self.info(500,u"第"+str(points_count+1)+u"个点")
            elif self.is_get_end_points:
                pos = QGraphicsSceneMouseEvent.scenePos()
                color = QColor(Qt.blue)
                self.addEllipse(QRectF(pos,QSizeF(5.0,5.0)),
                        QPen(color), QBrush(color))
                points_count = len(self.end_points)
                if points_count:
                    self.addLine(QLineF(self.end_points[points_count-1], pos))
                self.end_points.append(pos)
                self.info(500 ,u"第"+str(points_count+1)+u"个点")
            else:
                pass

    def info(self, interval=500,string=None):
        '''
        '''
        if string==None:
            if self.info_timer.isActive():
                if self.info_queue.empty():
                    self.info_item.setHtml('')
                    self.info_timer.stop()
                else:
                    info_text = self.info_queue.get()
                    self.info_item.setHtml(u'''
                            <h1 style="color:#003366;"><strong>%s</strong></h1>
                            ''' % info_text)
            else:
                self.info_timer.start(interval)
        else:
            if self.info_timer.isActive():
                self.info_queue.put(string)
            else:
                self.info_queue.put(string)
                self.info_timer.start(interval)

    def run(self):
        self.is_get_end_points = False
        self.is_get_initial_points = False

        initial_count = len(self.initial_points)
        end_count = len(self.end_points)
        if initial_count != end_count:
            self.info(500, u"起始点个数与终止点个数不等")
        else:
            self.animate_curve()

    def animate_curve(self):
        total_points = len(self.initial_points)
        self.all_points = self.interpolation(total_points)
        self.animate_count = 0
        self.animate_timer.start(100)

    def animate(self):
        each_points = self.all_points[self.animate_count]
        if self.animate_count:
            self.removeItem(self.line_group)

        self.line_group = QGraphicsItemGroup()
        for i in range(1, len(each_points)):
            self.line_group.addToGroup(QGraphicsLineItem( \
                    QLineF(each_points[i-1], each_points[i])))
        self.addItem(self.line_group)
        self.animate_count += 1
        if self.animate_count == len(self.all_points):
            self.animate_timer.stop()


    def restart(self):
        self.info_timer.stop()
        self.animate_timer.stop()
        self.clear()
        self.__setSpecialItem()
        self.__resetData()

    def interpolation(self, total_points, total_frame=30):
        result_points = []
        for i in range(1, total_frame+1):
            t = float(i)/total_frame
            per_frame_points = []
            # first point, use linear interpolation
            tmp_x0 = (1-t)*self.initial_points[0].x() + t*self.end_points[0].x()
            tmp_y0 = (1-t)*self.initial_points[0].y() + t*self.end_points[0].y()
            per_frame_points.append(QPointF(tmp_x0, tmp_y0))

            tmp_radian = 0
            for j in range(1, total_points):
                r0, radian0 = self.cal_2p_dis_radian(self.initial_points[j-1], \
                        self.initial_points[j])
                r1, radian1 = self.cal_2p_dis_radian(self.end_points[j-1], \
                        self.end_points[j])
                r = (1-t)*r0 + t*r1

                if abs(radian1-radian0) >= math.pi:
                    if radian1>radian0:
                        print "1>0", radian0*180/math.pi, radian1*180/math.pi
                        radian0 += 2 * math.pi
                    else:
                        print "0>1", radian0*180/math.pi, radian1*180/math.pi
                        radian1 += 2 * math.pi
                radian = (1-t)*radian0 + t*radian1

                point = self.cal_p_pos_by_relative(per_frame_points[j-1], \
                        r, radian)
                per_frame_points.append(point)

                tmp_radian = radian
            result_points.append(per_frame_points)
        return result_points
    
    def cal_2p_dis_radian(self, point_1, point_2): # calculate two point distance
        '''
        1. point_2 is on the right-top: dx>0 , dy<0, -pi/2<radian<0
        2. point_2 is on the right-bottom: dx>0, dy>0, pi/2>radian>0
        3. point_2 is on the left-top: dx<0, dy<0, -pi<radian<-pi/2 ||
            = -pi
        4. point_2 is on the left-bottom: dx<0, dy>0, pi/2<radian<pi
            = +pi 
            attention: can't be +pi
        '''
        dx = point_2.x() - point_1.x() 
        dy = point_2.y() - point_1.y()
        distance = math.sqrt(dx**2 + dy**2)
        if dx:
            radian = math.atan(dy*1.0/dx)
            if dx<0 and dy<0:
                radian += math.pi
                # radian -= math.pi
            elif dx<0 and dy>0:
                radian += math.pi
            elif dx>0 and dy<0:
                radian += 2*math.pi
        else:
            if dy<0:
                radian = -math.pi/2
            else:
                radian = math.pi/2
        return distance, radian
    
    def cal_p_pos_by_relative(self, r_point, r, radian):
        '''
        calculate point pos through the given relative_point pos and the r&radian
        return a QPointF Object
        '''
        dx = r*math.cos(radian)
        dy = r*math.sin(radian)
        x = r_point.x() + dx
        y = r_point.y() + dy
        return QPointF(x, y)
        

class MainView(QGraphicsView):
    def __init__(self, parent=None):
        super(MainView, self).__init__(parent)
        self.__initObjects()
        self.__setObjects()

    def __initObjects(self):
        self.scene = MainScene()

    def __setObjects(self):
        self.scene.setSceneRect(0, 0, self.size().width(),
                self.size().height())
        self.setScene(self.scene)
